import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类加载器
 */
public class ServerStartup extends ClassLoader {
	private File[] classDirs = null;
	private File[] jarFiles = null;
	private JarFile[] jars = null;
	private ClassLoader loader = null;

	/**
	 * 自动装载类目录下的所有文件（.class被认为是类文件，其它被则被认为是资源文件）以及 库目录下的jar文件。注意:
	 * classDirs路径会被递归调用，以加载其子目录下的文件，而 libDirs路径则不会
	 * 
	 * @param classDirs
	 *            放类文件和资源文件的目录
	 * @param libDirs
	 *            放jar文件的目录
	 */
	public ServerStartup(ClassLoader parent, String[] classDirs, String[] libDirs) {
		super(parent);
		loader = (parent == null) ? ClassLoader.getSystemClassLoader() : parent;
		File[] cds = null;
		if (classDirs != null) {
			cds = new File[classDirs.length];
			for (int i = 0; i < classDirs.length; i++) {
				cds[i] = ((classDirs[i] == null) ? null
						: new File(classDirs[i]));
			}
		}
		File[] lds = null;
		if (libDirs != null) {
			lds = new File[libDirs.length];
			for (int i = 0; i < libDirs.length; i++) {
				lds[i] = ((libDirs[i] == null) ? null : new File(libDirs[i]));
			}
		}
		addDirs(cds, lds);
	}

	/**
	 * 自动装载类目录下的所有文件（.class被认为是类文件，其它被则被认为是资源文件）以及 库目录下的jar文件。注意:
	 * classDirs路径会被递归调用，以加载其子目录下的文件，而 libDirs路径则不会
	 * 
	 * @param classDirs
	 *            放类文件和资源文件的目录
	 * @param libDirs
	 *            放jar文件的目录
	 */
	public ServerStartup(ClassLoader parent, File[] classDirs, File[] libDirs) {
		super(parent);
		loader = (parent == null) ? ClassLoader.getSystemClassLoader() : parent;
		addDirs(classDirs, libDirs);
	}

	/*     ********** ClassLoader Overrides ********** */
	public InputStream getResourceAsStream(String name) {
		InputStream stream = loader.getResourceAsStream(name);
		if (stream == null) {
			byte[] buf = loadBytes(name, false);
			if (buf != null) {
				stream = new ByteArrayInputStream(buf);
			}
		}
		return stream;
	}

	/**
	 * 依次按已装入类、父装载器、系统类、当前加载的类搜索
	 */
	public Class loadClass(String name, boolean resolve)
			throws ClassNotFoundException {

		Class clazz = null;
		clazz = findLoadedClass(name);

		if (clazz == null) {
			try {
				clazz = loader.loadClass(name);
			} catch (Throwable e) {
			}
		}

		if (clazz == null) {
			// System.err.println( " ***** " + name );
			try {
				byte[] bb = loadBytes(name, true);
				// System.err.println( "   ***** " + name + " = " + bb );
				if (bb != null) {
					clazz = findClassEx(name, bb);
				}
			} catch (Exception e) {
			}
		}

		if (clazz == null) {
			throw new ClassNotFoundException(name);
		}
		if (resolve && clazz != null) {
			resolveClass(clazz);

		}
		return clazz;
	}

	protected URL findResource(String name) {
		URL url = loader.getResource(name);
		if (url != null)
			return url;

		if (classDirs != null) {
			for (int i = 0; i < classDirs.length; i++) {
				File f = new File(classDirs[i], name);
				if (f.isFile() || f.exists() || f.canRead())
					try {
						String s = "file:///" + f.getCanonicalPath();
						// System.err.println( " resource name = " + s );
						return new URL(s);
					} catch (IOException e) {
						System.err.println("url error");
						return null;
					}
			}
		}
		if (jars != null) {
			for (int i = 0; i < jars.length; i++) {
				JarEntry entry = jars[i].getJarEntry(name);
				if (entry != null)
					try {
						String s = "jar:file:///"
								+ jarFiles[i].getCanonicalPath() + "!/" + name;
						// System.err.println( s );
						return new URL(s);
					} catch (IOException e) {
						System.err.println("url error");
						return null;
					}
			}
		}
		return null;
	}

	private void addDirs(File[] classDirs, File[] libDirs) {
		ArrayList al = new ArrayList();
		if (classDirs != null) {
			for (int i = 0; i < classDirs.length; i++) {
				File f = checkDir(classDirs[i]);
				if (f != null) {
					al.add(f);
				}
			}
			this.classDirs = (File[]) al.toArray(new File[al.size()]);
		}
		al.clear();
		ArrayList jarFileList = new ArrayList();
		if (libDirs != null) {
			for (int i = 0; i < libDirs.length; i++) {
				File one = checkDir(libDirs[i]);
				if (one == null) {
					continue;
				}
				File[] files = one.listFiles();
				for (int j = 0; j < files.length; j++) {
					String name = files[j].getName();
					// 加载.jar与.zip后缀的包
					if (!files[j].isDirectory() && files[j].canRead()
							&& (name.endsWith(".jar") || name.endsWith(".zip"))) {
						try {
							jarFileList.add(files[j]);
							al.add(new JarFile(files[j]));
						} catch (IOException e) {
							e.printStackTrace();
							System.out.println("不能加载包文件" + name);
						}
					} // if
				} // for j
			} // for i
			this.jarFiles = (File[]) jarFileList.toArray(new File[jarFileList
					.size()]);
			this.jars = (JarFile[]) al.toArray(new JarFile[al.size()]);
		} // if
	}

	private synchronized Class findClassEx(String name, byte[] bb)
			throws ClassNotFoundException {
		Class clazz = findLoadedClass(name);
		if (clazz != null) {
			return clazz;
		}

		if (bb == null) {
			throw new ClassNotFoundException(name);
		}
		return defineClass(name, bb, 0, bb.length);
	}

	private byte[] loadBytes(String name, boolean isClass) {
		// Logger.info(name);
		byte[] bb = loadFromClassDirs(name, isClass);
		if (bb != null) {
			return bb;
		}
		return loadFromJars(name, isClass);
	}

	private byte[] loadFromClassDirs(String name, boolean isClass) {
		if (classDirs == null) {
			return null;
		}
		if (isClass) {
			name = name.replace('.', File.separatorChar) + ".class";
		}
		for (int i = 0; i < classDirs.length; i++) {
			File f = new File(classDirs[i], name);
			if (f.isFile() || f.exists() || f.canRead()) {
				byte[] bb = getFileBytes(f);
				if (bb != null) {
					return bb;
				}
			}
		}
		return null;
	}

	private byte[] loadFromJars(String name, boolean isClass) {
		if (jars == null) {
			return null;
		}
		if (isClass) {
			name = name.replace('.', '/') + ".class";
		}
		for (int i = 0; i < jars.length; i++) {
			byte[] bb = getFileBytes(jars[i], name);
			if (bb != null) {
				return bb;
			}
		}
		return null;
	}

	private static File checkDir(File dir) {
		if (dir == null) {
			System.out.println(dir + "为空");
			return null;
		}
		if (!dir.isDirectory() || !dir.exists() || !dir.canRead()) {
			// System.out.println( dir + "不是一个目录，或不存在，或无读权限" );
			return null;
		}
		return dir;
	}

	// 从文件中读数据
	private byte[] getFileBytes(File f) {
		try {
			int len = (int) f.length();
			byte[] data = new byte[len];
			BufferedInputStream bis = new BufferedInputStream(
					new FileInputStream(f));
			int i = 0, offset = 0;
			while (i >= 0 && offset < len) {
				i = bis.read(data, offset, len - offset);
				offset += i;
			}
			bis.close();
			return data;
		} catch (IOException e) {
			System.out.println("读以下文件时出错：" + f);
			return null;
		}
	}

	// 从jar文件的入口中读数据
	private static byte[] getFileBytes(JarFile jar, String name) {
		try {
			JarEntry entry = jar.getJarEntry(name);
			if (entry == null) {
				return null;
			}
			int len = (int) entry.getSize();
			byte[] data = new byte[len];
			BufferedInputStream bis = new BufferedInputStream(jar
					.getInputStream(entry));
			int i = 0, offset = 0;
			while (i >= 0 && offset < len) {
				i = bis.read(data, offset, len - offset);
				offset += i;
			}
			bis.close();
			return data;
		} catch (IOException e) {
			System.out.println("读以下文件时出错：" + jar);
			return null;
		}
	}

	public static void main(String args[]) {
		try {
			int argsLen = args.length;
			if(argsLen<1){
				throw new Exception("You must type a arg for start class!");
			}
			String classDir[] = new String[1];
			classDir[0] = "classes";
			String libDir[] = new String[1];
			libDir[0] = "lib";
			ServerStartup scl = new ServerStartup(ClassLoader
					.getSystemClassLoader(), classDir, libDir);
			Thread.currentThread().setContextClassLoader(scl);
			Class clas = scl.loadClass(args[0]);
			Class mainArgType[] = { String[].class };
			Method main = clas.getMethod("main", mainArgType);
			
			String[] realArgs = {};
			if(argsLen>1){
				realArgs = new String[argsLen-1];
				System.arraycopy(args, 1, realArgs, 0, argsLen-1);
			}			
			
			main.invoke(null, new Object[] { realArgs });
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}
